#pragma once

#include <atomic>
#include <condition_variable>
#include <functional>
#include <memory>
#include <mutex>
#include <queue>
#include <thread>
#include <vector>
#include <algorithm>
#include "Logger.h"

namespace zeroplus::mq
{

    class ThreadPool final
    {
    public:
        using TaskCallback = std::function<void()>;

    private:
        struct Impl
        {
            std::string name;
            std::vector<std::thread> threads;
            std::queue<TaskCallback> taskQueue;
            bool isShutdown{false};
            mutable std::mutex mutex;
            std::condition_variable condition;
        };
        const size_t maxQueueSize_;
        std::unique_ptr<Impl> impl_;

    public:
        explicit ThreadPool(size_t threadCount, size_t maxQueueSize = 10000)
            : maxQueueSize_(maxQueueSize),
              impl_(std::make_unique<Impl>())
        {
            Initialize(threadCount);
        }

        ~ThreadPool() { Shutdown(); }

        void SetName(const std::string &name)
        {
            std::lock_guard lock(impl_->mutex);
            impl_->name = name;
        }

        std::string GetName() const
        {
            std::lock_guard lock(impl_->mutex);
            return impl_->name;
        }

        bool PostTask(const TaskCallback &task)
        {
            std::unique_lock lock(impl_->mutex);
            if (impl_->isShutdown || IsFull())
            {
                return false;
            }

            impl_->taskQueue.emplace(task);
            lock.unlock();
            impl_->condition.notify_one();
            return true;
        }

        size_t GetTaskCount() const
        {
            std::lock_guard lock(impl_->mutex);
            return impl_->taskQueue.size();
        }

        bool IsInWorkThread() const
        {
            auto currentId = std::this_thread::get_id();
            std::lock_guard lock(impl_->mutex);
            return std::any_of(impl_->threads.begin(), impl_->threads.end(),
                               [&currentId](const std::thread &t)
                               {
                                   return t.get_id() == currentId;
                               });
        }

        bool Shutdown()
        {
            {
                std::lock_guard lock(impl_->mutex);
                if (impl_->isShutdown)
                    return false;
                impl_->isShutdown = true;
            }

            impl_->condition.notify_all();
            for (auto &thread : impl_->threads)
            {
                if (thread.joinable())
                {
                    thread.join();
                }
            }
            return true;
        }

        bool IsFull() const
        {
            return impl_->taskQueue.size() >= maxQueueSize_;
        }

    private:
        void Initialize(size_t threadCount)
        {
            impl_->threads.reserve(threadCount);
            for (size_t i = 0; i < threadCount; ++i)
            {
                impl_->threads.emplace_back([this]
                                            { WorkerThread(); });
            }
        }

        void WorkerThread()
        {
            while (true)
            {
                TaskCallback task;
                {
                    std::unique_lock lock(impl_->mutex);
                    impl_->condition.wait(lock, [this]
                                          { return !impl_->taskQueue.empty() || impl_->isShutdown; });

                    if (impl_->isShutdown && impl_->taskQueue.empty())
                    {
                        break;
                    }

                    if (!impl_->taskQueue.empty())
                    {
                        task = std::move(impl_->taskQueue.front());
                        impl_->taskQueue.pop();
                    }
                }
                if (task)
                    task();
            }
        }
    };

} // namespace zeroplus::mq